use rust_decimal::prelude::*;
use rust_decimal::Decimal;
use serde::Deserialize;

/// =======================
/// PositionRaw → Position
/// =======================

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PositionRaw {
    pub symbol: String,
    pub position_side: String,
    pub position_amt: String,
    pub entry_price: String,
    pub break_even_price: String,
    pub mark_price: String,
    pub un_realized_profit: String,
    pub liquidation_price: String,
    pub isolated_margin: String,
    pub notional: String,
    pub margin_asset: String,
    pub isolated_wallet: String,
    pub initial_margin: String,
    pub maint_margin: String,
    pub position_initial_margin: String,
    pub open_order_initial_margin: String,
    pub adl: i32,
    pub bid_notional: String,
    pub ask_notional: String,
    pub update_time: u64,
}

impl PositionRaw {
    pub fn to_model(&self) -> Option<Position> {
        Some(Position {
            symbol: self.symbol.clone(),
            position_side: self.position_side.clone(),
            position_amt: self.position_amt.parse().ok()?,
            entry_price: self.entry_price.parse().ok()?,
            break_even_price: self.break_even_price.parse().ok()?,
            mark_price: self.mark_price.parse().ok()?,
            unrealized_profit: self.un_realized_profit.parse().ok()?,
            liquidation_price: self.liquidation_price.parse().ok()?,
            isolated_margin: self.isolated_margin.parse().ok()?,
            notional: self.notional.parse().ok()?,
            margin_asset: self.margin_asset.clone(),
            isolated_wallet: self.isolated_wallet.parse().ok()?,
            initial_margin: self.initial_margin.parse().ok()?,
            maint_margin: self.maint_margin.parse().ok()?,
            position_initial_margin: self.position_initial_margin.parse().ok()?,
            open_order_initial_margin: self.open_order_initial_margin.parse().ok()?,
            adl: self.adl,
            bid_notional: self.bid_notional.parse().ok()?,
            ask_notional: self.ask_notional.parse().ok()?,
            update_time: self.update_time,
        })
    }
}

#[derive(Debug, Clone)]
pub struct Position {
    pub symbol: String,
    pub position_side: String,
    pub position_amt: Decimal,
    pub entry_price: Decimal,
    pub break_even_price: Decimal,
    pub mark_price: Decimal,
    pub unrealized_profit: Decimal,
    pub liquidation_price: Decimal,
    pub isolated_margin: Decimal,
    pub notional: Decimal,
    pub margin_asset: String,
    pub isolated_wallet: Decimal,
    pub initial_margin: Decimal,
    pub maint_margin: Decimal,
    pub position_initial_margin: Decimal,
    pub open_order_initial_margin: Decimal,
    pub adl: i32,
    pub bid_notional: Decimal,
    pub ask_notional: Decimal,
    pub update_time: u64,
}

/// ====================
/// AssetRaw → Asset
/// ====================

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AssetRaw {
    pub asset: String,
    pub wallet_balance: String,
    pub unrealized_profit: String,
    pub margin_balance: String,
    pub maint_margin: String,
    pub initial_margin: String,
    pub position_initial_margin: String,
    pub open_order_initial_margin: String,
    pub cross_wallet_balance: String,
    pub cross_un_pnl: String,
    pub available_balance: String,
    pub max_withdraw_amount: String,
    pub update_time: u64,
}

impl AssetRaw {
    pub fn to_model(&self) -> Option<Asset> {
        Some(Asset {
            asset: self.asset.clone(),
            wallet_balance: self.wallet_balance.parse().ok()?,
            unrealized_profit: self.unrealized_profit.parse().ok()?,
            margin_balance: self.margin_balance.parse().ok()?,
            maint_margin: self.maint_margin.parse().ok()?,
            initial_margin: self.initial_margin.parse().ok()?,
            position_initial_margin: self.position_initial_margin.parse().ok()?,
            open_order_initial_margin: self.open_order_initial_margin.parse().ok()?,
            cross_wallet_balance: self.cross_wallet_balance.parse().ok()?,
            cross_un_pnl: self.cross_un_pnl.parse().ok()?,
            available_balance: self.available_balance.parse().ok()?,
            max_withdraw_amount: self.max_withdraw_amount.parse().ok()?,
            update_time: self.update_time,
        })
    }
}

#[derive(Debug, Clone)]
pub struct Asset {
    pub asset: String,
    pub wallet_balance: Decimal,
    pub unrealized_profit: Decimal,
    pub margin_balance: Decimal,
    pub maint_margin: Decimal,
    pub initial_margin: Decimal,
    pub position_initial_margin: Decimal,
    pub open_order_initial_margin: Decimal,
    pub cross_wallet_balance: Decimal,
    pub cross_un_pnl: Decimal,
    pub available_balance: Decimal,
    pub max_withdraw_amount: Decimal,
    pub update_time: u64,
}

/// =======================
/// AccountRaw → Account
/// =======================

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountRaw {
    pub total_initial_margin: String,
    pub total_maint_margin: String,
    pub total_wallet_balance: String,
    pub total_unrealized_profit: String,
    pub total_margin_balance: String,
    pub total_position_initial_margin: String,
    pub total_open_order_initial_margin: String,
    pub total_cross_wallet_balance: String,
    pub total_cross_un_pnl: String,
    pub available_balance: String,
    pub max_withdraw_amount: String,
    pub assets: Vec<AssetRaw>,
    pub positions: Vec<PositionRaw>,
}

impl AccountRaw {
    pub fn to_model(&self) -> Option<Account> {
        Some(Account {
            total_initial_margin: self.total_initial_margin.parse().ok()?,
            total_maint_margin: self.total_maint_margin.parse().ok()?,
            total_wallet_balance: self.total_wallet_balance.parse().ok()?,
            total_unrealized_profit: self.total_unrealized_profit.parse().ok()?,
            total_margin_balance: self.total_margin_balance.parse().ok()?,
            total_position_initial_margin: self.total_position_initial_margin.parse().ok()?,
            total_open_order_initial_margin: self.total_open_order_initial_margin.parse().ok()?,
            total_cross_wallet_balance: self.total_cross_wallet_balance.parse().ok()?,
            total_cross_un_pnl: self.total_cross_un_pnl.parse().ok()?,
            available_balance: self.available_balance.parse().ok()?,
            max_withdraw_amount: self.max_withdraw_amount.parse().ok()?,
            assets: self.assets.iter().filter_map(|a| a.to_model()).collect(),
            positions: self.positions.iter().filter_map(|p| p.to_model()).collect(),
        })
    }
}

#[derive(Debug, Clone)]
pub struct Account {
    pub total_initial_margin: Decimal,
    pub total_maint_margin: Decimal,
    pub total_wallet_balance: Decimal,
    pub total_unrealized_profit: Decimal,
    pub total_margin_balance: Decimal,
    pub total_position_initial_margin: Decimal,
    pub total_open_order_initial_margin: Decimal,
    pub total_cross_wallet_balance: Decimal,
    pub total_cross_un_pnl: Decimal,
    pub available_balance: Decimal,
    pub max_withdraw_amount: Decimal,
    pub assets: Vec<Asset>,
    pub positions: Vec<Position>,
}

/// ========================
/// LeverageBracketRaw → LeverageBracket
/// ========================

#[derive(Debug, Deserialize)]
pub struct LeverageBracketRaw {
    pub symbol: String,
    #[serde(default)]
    pub notionalCoef: Option<Decimal>,
    pub brackets: Vec<LeverageBracketDetailRaw>,
}

#[derive(Debug, Deserialize)]
pub struct LeverageBracketDetailRaw {
    pub bracket: u32,
    pub initialLeverage: u32,
    pub notionalCap: Decimal,
    pub notionalFloor: Decimal,
    pub maintMarginRatio: Decimal,
    pub cum: u32,
}

impl LeverageBracketRaw {
    pub fn to_model(&self) -> LeverageBracket {
        LeverageBracket {
            symbol: self.symbol.clone(),
            notional_coef: self.notionalCoef,
            brackets: self.brackets.iter().map(|b| b.to_model()).collect(),
        }
    }
}

impl LeverageBracketDetailRaw {
    pub fn to_model(&self) -> LeverageBracketDetail {
        LeverageBracketDetail {
            bracket: self.bracket,
            initial_leverage: self.initialLeverage,
            notional_cap: self.notionalCap,
            notional_floor: self.notionalFloor,
            maint_margin_ratio: self.maintMarginRatio,
            cum: self.cum,
        }
    }
}

/// ========================
/// LeverageBracket
/// ========================

#[derive(Debug, Clone)]
pub struct LeverageBracket {
    pub symbol: String,
    pub notional_coef: Option<Decimal>,
    pub brackets: Vec<LeverageBracketDetail>,
}

#[derive(Debug, Clone)]
pub struct LeverageBracketDetail {
    pub bracket: u32,
    pub initial_leverage: u32,
    pub notional_cap: Decimal,
    pub notional_floor: Decimal,
    pub maint_margin_ratio: Decimal,
    pub cum: u32,
}

/// ========================
/// BalanceItemRaw → BalanceItem
/// ========================

#[derive(Debug, Deserialize)]
pub struct BalanceItemRaw {
    pub accountAlias: String,
    pub asset: String,
    pub balance: String,
    pub crossWalletBalance: String,
    pub crossUnPnl: String,
    pub availableBalance: String,
    pub maxWithdrawAmount: String,
    pub marginAvailable: bool,
    pub updateTime: u64,
}

impl BalanceItemRaw {
    pub fn to_model(&self) -> Option<BalanceItem> {
        Some(BalanceItem {
            account_alias: self.accountAlias.clone(),
            asset: self.asset.clone(),
            balance: self.balance.parse().ok()?,
            cross_wallet_balance: self.crossWalletBalance.parse().ok()?,
            cross_unrealized_pnl: self.crossUnPnl.parse().ok()?,
            available_balance: self.availableBalance.parse().ok()?,
            max_withdraw_amount: self.maxWithdrawAmount.parse().ok()?,
            margin_available: self.marginAvailable,
            update_time: self.updateTime,
        })
    }
}

/// ========================
/// BalanceItem
/// ========================

#[derive(Debug, Clone)]
pub struct BalanceItem {
    pub account_alias: String,
    pub asset: String,
    pub balance: Decimal,
    pub cross_wallet_balance: Decimal,
    pub cross_unrealized_pnl: Decimal,
    pub available_balance: Decimal,
    pub max_withdraw_amount: Decimal,
    pub margin_available: bool,
    pub update_time: u64,
}

/// ========================
/// IncomeRecordRaw → IncomeRecord
/// ========================

#[derive(Debug, Deserialize)]
pub struct IncomeRecordRaw {
    #[serde(default)]
    pub symbol: String,
    pub incomeType: String,
    pub income: String,
    pub asset: String,
    pub info: String,
    pub time: u64,
    pub tranId: u64,
    #[serde(default)]
    pub tradeId: String,
}

impl IncomeRecordRaw {
    pub fn to_model(&self) -> Option<IncomeRecord> {
        Some(IncomeRecord {
            symbol: self.symbol.clone(),
            income_type: self.incomeType.clone(),
            income: self.income.parse().ok()?,
            asset: self.asset.clone(),
            info: self.info.clone(),
            time: self.time,
            tran_id: self.tranId,
            trade_id: self.tradeId.clone(),
        })
    }
}

/// ========================
/// IncomeRecord
/// ========================

#[derive(Debug, Clone)]
pub struct IncomeRecord {
    pub symbol: String,
    pub income_type: String,
    pub income: Decimal,
    pub asset: String,
    pub info: String,
    pub time: u64,
    pub tran_id: u64,
    pub trade_id: String,
}
